import { isSameVNode } from ".";


function createComponent(vnode) {
  let i = vnode.data;
  if (i = i.hook && (i = i.init)) {
    // 去舒适化组件
    i(vnode);
  }
}
// 开始创建真实元素
export function createElm(vnode) {
  const { tag, data, children, text } = vnode;
  if (typeof tag === 'string') {
    // 如果是字符串,那就是标签
    // 大叔大婶
    // first分支新增代码
    // 创建真实元素,也要区分组件还是元素
    if (createComponent(vnode)) {
      // 组件
      return vnode.componentInstance.$el
    }
    // 将真实dom保存到虚拟dom中,方便后续diff算法
    vnode.el = document.createElement(tag);
    // 更新属性更新vnode.el身上的data属性
    patchProps(vnode.el, {}, data);
    if (children) {
      children.forEach(child => {
        // 将返回值插入到父节点的尾部
        vnode.el.appendChild(createElm(child));
      })
    }
  } else {
    // 将真实dom保存到虚拟dom中,方便后续diff算法
    vnode.el = document.createTextNode(text);
  }
  // 返回真实dom
  return vnode.el;
}


// 更新属性更新el身上的props属性
function patchProps(el, oldProps = {}, props = {}) {
  // 有可能老的属性有点用不到了得删除
  const oldStyle = oldProps.style || {};
  const newStyle = props.style || {};
  // 对比新旧不要了的style
  for (let key in oldStyle) {
    if (!newStyle[key]) {
      el.style[key] = '';
    }
  }
  // 对比新旧不要了的attr
  for (let key in oldProps) {
    if (!props[key]) {
      el.removeAttribute(key);
    }
  }
  for (let key in props) {
    if (key === 'style') {
      // 如果是样式就再循环加上去
      for (let styleName in props.style) {
        el.style[styleName] = props.style[styleName];
      }
    } else {
      // 否则直接添加属性
      el.setAttribute(key, props[key]);
    }
  }
}

export function patch(oldVNode, vnode) {
  if(!oldVNode){
    // 如果不存在就是组件的挂载
    return createElm(vnode);
  }
  // 看看oldVNode是什么
  const isRealElement = oldVNode.nodeType;
  if (isRealElement) {
    // 如果oldVNode是真实元素,说明是初次渲染
    // 获取真实元素
    const elm = oldVNode;
    // 获取父元素
    const parentElm = elm.parentNode;
    const newElm = createElm(vnode);
    // 把新的放在老节点下面
    parentElm.insertBefore(newElm, elm.nextSibling);
    // 删除老节点
    parentElm.removeChild(elm);
    return newElm;
  } else {
    // 开始diff算法
    return patchVNode(oldVNode, vnode);
  }
}

// 更新vnode
function patchVNode(oldVNode, vnode) {
  // 两个节点tag/key不一样直接不比了
  // 一样就比较内部的差异
  if (!isSameVNode(oldVNode, vnode)) {
    // 不是就直接替换了
    const el = createElm(vnode);
    oldVNode.el.parentNode.replaceChild(el, oldVNode.el);
    return el;
  };
  // 复用老vnode的真实dom
  const el = vnode.el = oldVNode.el;
  // 是否是文本节点
  if (!oldVNode.tag) {
    // 没有tag就说明是文本节点
    if (oldVNode.text !== vnode.text) {
      // 用新的文本覆盖老的
      el.textContent = vnode.text;
    }
    return el;
  }
  // 是标签更新标签的属性
  patchProps(el, oldVNode.data, vnode.data);
  const oldChildren = oldVNode.children || [];
  const newChildren = vnode.children || [];
  if (oldChildren.length > 0 && newChildren.length > 0) {
    // 双方都有儿子,开始diff算法
    updateChildren(el, oldChildren, newChildren);
  } else if (newChildren.length > 0) {
    // 没老的有新的直接挂载新的
    mountChildren(el, newChildren);
  } else {
    // 没有新的老的干掉
    unmountedChildren(el, oldChildren);
  }
  return el;
}

// 挂载儿子
function mountChildren(el, newChildren) {
  newChildren.forEach(child => {
    el.appendChild(createElm(child));
  })
}
// 删除儿子
function unmountedChildren(el, oldChildren) {
  oldChildren.forEach(child => {
    el.removeChild(child.el);
  })
}
// 更新儿子
function updateChildren(el, oldChildren, newChildren) {
  let oldStartIndex = 0;
  let newStartIndex = 0;
  let oldEndIndex = oldChildren.length - 1;
  let newEndIndex = newChildren.length - 1;
  let oldStartVNode = oldChildren[oldStartIndex];
  let newStartVNode = newChildren[newStartIndex];
  let oldEndVNode = oldChildren[oldEndIndex];
  let newEndVNode = newChildren[newEndIndex];
  // 生成索引表 
  function makeIndexByKey(children) {
    const map = {};
    children.forEach((child, index) => {
      map[child.key] = index;
    })
    return map;
  }
  const map = makeIndexByKey(oldChildren);
  // 双方有一个头指针大于尾指针就停止循环
  while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    if (!oldStartVNode) {
      oldStartVNode = oldChildren[++oldStartIndex];
    } else if (!oldEndVNode) {
      oldEndVNode = oldChildren[--oldEndIndex];
    }
    if (isSameVNode(oldStartVNode, newStartVNode)) {
      // 正着一个个比
      // 如果是一样的节点,则递归比较子节点
      patchVNode(oldStartVNode, newStartVNode);
      oldStartVNode = oldChildren[++oldStartIndex];
      newStartVNode = newChildren[++newStartIndex];
    } else if (isSameVNode(oldEndVNode, newEndVNode)) {
      // 反着一个个比
      // 如果是一样的节点,则递归比较子节点
      patchVNode(oldEndVNode, newEndVNode);
      oldEndVNode = oldChildren[--oldEndIndex];
      newEndVNode = newChildren[--newEndIndex];
    } else if (isSameVNode(oldEndVNode, newStartVNode)) {
      // 交叉对比
      patchVNode(oldEndVNode, newStartVNode);
      // 进行移动,将老尾移到老头前面面去
      el.insertBefore(oldEndVNode.el, oldStartVNode.el);
      oldEndVNode = oldChildren[--oldEndIndex];
      newStartVNode = newChildren[++newStartIndex];

    } else if (isSameVNode(oldStartVNode, newEndVNode)) {
      // 交叉对比
      patchVNode(oldStartVNode, newEndVNode);
      // 进行移动,将老头移到老尾后面去
      el.insertBefore(oldStartVNode.el, oldEndVNode.el.nextSibling);
      oldStartVNode = oldChildren[++oldStartIndex];
      newEndVNode = newChildren[--newEndIndex];
    } else {
      // 用新头去老child里找,找到了就移到旧头指针最前面,同时干掉这个旧节点,没找到就插入到旧节点最前面
      // 如果找到了就是要移动的,否则就是新增的
      const moveIndex = map[newStartVNode.key];
      if (moveIndex !== undefined) {
        // 找到对应的旧的
        const moveVNode = oldChildren[moveIndex];
        el.insertBefore(moveVNode.el, oldStartVNode.el);
        oldChildren[moveIndex] = undefined;
        patchVNode(moveVNode, newStartVNode);
      } else {
        // 新增
        el.insertBefore(createElm(newStartVNode), oldStartVNode.el);
      }
      newStartVNode = newChildren[++newStartIndex];
    }
  }
  if (newStartIndex <= newEndIndex) {
    // 如果新结束索引比开始索引大,说明有新的节点没被遍历到,直接填就行了
    for (let i = newStartIndex; i <= newEndIndex; i++) {
      // 把新的虚拟节点变成真实节点插入父级中
      const childEl = createElm(newChildren[i]);
      // 这里有可能是往前插,也有可能往后插
      // 如果newEndIndex+1能取到值就是往前,否则就是往后
      let anchor = newChildren[newEndIndex + 1] ? newChildren[newEndIndex + 1].el : null;
      // anchor为null默认向尾部加
      el.insertBefore(childEl, anchor);
    }
  }
  if (oldStartIndex <= oldEndIndex) {
    // 如果旧结束索引比开始索引大,说明有旧的节点多余了,开始删除
    for (let i = oldStartIndex; i <= oldEndIndex; i++) {
      // 开删
      if (oldChildren[i]) {
        unmountedChildren(el, [oldChildren[i]]);
      }
    }
  }
}